﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace dPet.Domain.Models
{
	using dPet.Domain.Events;
	using Hangerd;
	using Hangerd.Entity;
	using Hangerd.Event;

	public class Product : EntityBase
	{
		#region Public Properties

		/// <summary>
		/// 所属医院ID
		/// </summary>
		public string HospitalId { get; private set; }

		/// <summary>
		/// 大类
		/// </summary>
		public virtual ProductCategory Category { get; private set; }

		/// <summary>
		/// 小类
		/// </summary>
		public virtual ProductSubCategory SubCategory { get; private set; }

		/// <summary>
		/// 条码
		/// </summary>
		[RecordModify]
		public string Barcode { get; set; }


		private string _name;

		/// <summary>
		/// 商品名称
		/// </summary>
		[RecordModify]
		public string Name
		{
			get { return _name; }
			set
			{
				if (string.IsNullOrWhiteSpace(value))
				{
					throw new HangerdException("商品名称不可为空");
				}

				_name = value.Trim();
			}
		}


		private decimal _unitPrice;

		/// <summary>
		/// 单价
		/// </summary>
		[RecordModify]
		public decimal UnitPrice
		{
			get { return _unitPrice; }
			set
			{
				if (value < 0)
				{
					throw new HangerdException("商品单价不可小于0");
				}

				_unitPrice = value;
			}
		}

		/// <summary>
		/// 单位名称
		/// </summary>
		[RecordModify]
		public string UnitName { get; set; }

		/// <summary>
		/// 是否启用库存数量告警
		/// </summary>
		[RecordModify]
		public bool EnableQuantityAlarm { get; private set; }

		/// <summary>
		/// 库存数量告警阈值
		/// </summary>
		[RecordModify]
		public int QuantityAlarmThreshold { get; private set; }

		/// <summary>
		/// 是否启用库存过期告警
		/// </summary>
		[RecordModify]
		public bool EnableExpiryAlarm { get; private set; }

		/// <summary>
		/// 库存过期告警阈值（天）
		/// </summary>
		[RecordModify]
		public int ExpiryAlarmThreshold { get; private set; }

		/// <summary>
		/// 商品查询标签 
		/// </summary>
		[RecordModify]
		public string QueryTag { get; set; }

		/// <summary>
		/// 库存列表
		/// </summary>
		public virtual ICollection<ProductInventory> Inventories { get; private set; }

		/// <summary>
		/// 创建时间
		/// </summary>
		public DateTime CreateTime { get; private set; }

		#endregion

		#region Constructors

		private Product() { }

		public Product(Hospital hospital, string name)
		{
			this.HospitalId = hospital.Id;
			this.Name = name;
			this.Inventories = new List<ProductInventory>();
			this.CreateTime = DateTime.Now;
		}

		#endregion

		#region Public Methods

		/// <summary>
		/// 设置类别
		/// </summary>
		public void Categorize(ProductCategory category, ProductSubCategory subCategory)
		{
			if (subCategory != null)
			{
				this.Category = subCategory.Category;
				this.SubCategory = subCategory;
			}
			else
			{
				this.Category = category;
				this.SubCategory = subCategory;
			}
		}

		//设置数量告警
		public void SetQuantityAlarmConfig(bool enable, int? threshold)
		{
			if (threshold.HasValue)
			{
				if (threshold < 0)
				{
					throw new HangerdException("库存数量告警阈值不可小于0");
				}

				this.QuantityAlarmThreshold = threshold.Value;
			}

			//由开到关，发布禁用事件
			if (!enable && enable != this.EnableQuantityAlarm)
			{
				DomainEvent.Publish(new InventoryAlarmDisabledEvent(this, InventoryAlarmType.Quantity));
			}

			this.EnableQuantityAlarm = enable;
		}

		//设置过期告警
		public void SetExpiryAlarmConfig(bool enable, int? threshold)
		{
			if (threshold.HasValue)
			{
				if (threshold < 0)
				{
					throw new HangerdException("库存过期告警阈值不可小于0");
				}

				this.ExpiryAlarmThreshold = threshold.Value;
			}

			//由开到关，发布禁用事件
			if (!enable && enable != this.EnableExpiryAlarm)
			{
				DomainEvent.Publish(new InventoryAlarmDisabledEvent(this, InventoryAlarmType.Expiry));
			}

			this.EnableExpiryAlarm = enable;
		}

		/// <summary>
		/// 根据库存编号入库
		/// </summary>
		public void AddInventory(string inventoryNumber, int quantity)
		{
			var inventory = this.GetInventoryByInventoryNumber(inventoryNumber);

			if (inventory == null)
			{
				throw new HangerdException("商品:{0} 不存在库存编号: {1}", this.Name, inventoryNumber);
			}

			inventory.Add(quantity);
		}

		/// <summary>
		/// 商品入库
		/// </summary>
		public void AddInventory(DateTime expiryTime, int quantity)
		{
			//根据过期时间筛选库存记录
			var inventory = this.Inventories
				.FirstOrDefault(i => i.ExpiryTime == expiryTime);

			if (inventory == null)
			{
				inventory = new ProductInventory(this, expiryTime);

				this.Inventories.Add(inventory);
			}

			inventory.Add(quantity);
		}

		/// <summary>
		/// 商品出库
		/// </summary>
		public void ReduceInventory(OrderBase order, int quantity)
		{
			//查找有效库存，优先选择过期时间早的库存
			var validInventories = this.Inventories.Where(i => i.Quantity > 0).OrderBy(i => i.ExpiryTime);
			var quantityWait = quantity;

			foreach (var inventory in validInventories)
			{
				var reduceQuantity = Math.Min(inventory.Quantity, quantityWait);

				//减掉相应库存
				inventory.Reduce(reduceQuantity);

				//发布削减库存事件
				DomainEvent.Publish(new InventoryReducedEvent(inventory, reduceQuantity, order));

				quantityWait -= reduceQuantity;

				//该批库存中数量足够则直接跳出，不够则检查下批库存
				if (quantityWait <= 0)
				{
					break;
				}
			}

			if (quantityWait > 0)
			{
				throw new HangerdException("商品:{0} 库存不足", this.Name);
			}
		}

		/// <summary>
		/// 库存报损
		/// </summary>
		public void ReportInventoryLoss(string inventoryNumber, int quantity, string @operator)
		{
			if (quantity < 1)
			{
				throw new HangerdException("报损数量须大于0");
			}

			var inventory = this.GetInventoryByInventoryNumber(inventoryNumber);

			if (inventory == null)
			{
				throw new HangerdException("商品:{0} 不存在库存编号: {1}", this.Name, inventoryNumber);
			}

			inventory.Reduce(quantity);

			//发布库存报损事件
			DomainEvent.Publish(new InventoryLOReportedEvent(inventory, InventoryLOType.Loss, quantity, @operator));
		}

		/// <summary>
		/// 库存报溢
		/// </summary>
		public void ReportInventoryOverflow(string inventoryNumber, int quantity, string @operator)
		{
			if (quantity < 1)
			{
				throw new HangerdException("报溢数量须大于0");
			}

			var inventory = this.GetInventoryByInventoryNumber(inventoryNumber);

			if (inventory == null)
			{
				throw new HangerdException("商品:{0} 不存在库存编号: {1}", this.Name, inventoryNumber);
			}

			inventory.Add(quantity);

			//发布库存报溢事件
			DomainEvent.Publish(new InventoryLOReportedEvent(inventory, InventoryLOType.Overflow, quantity, @operator));
		}

		/// <summary>
		/// 库存余量检测
		/// </summary>
		public void CheckInventoryQuantity()
		{
			if (!this.EnableQuantityAlarm || this.Inventories == null)
			{
				return;
			}

			var inventoryTotal = this.Inventories.Where(i => i.Quantity > 0).Sum(i => i.Quantity);

			InventoryAlarmItem alarmItem = null;

			if (inventoryTotal <= this.QuantityAlarmThreshold)
			{
				var alarmDetail = string.Format("当前库存量（{0}）达到设定阈值（{1}）", inventoryTotal, this.QuantityAlarmThreshold);

				alarmItem = new InventoryAlarmItem(this, InventoryAlarmType.Quantity, alarmDetail);
			}

			DomainEvent.Publish(new InventoryCheckedEvent(this, InventoryAlarmType.Quantity, alarmItem));
		}

		/// <summary>
		/// 库存过期检测
		/// </summary>
		public void CheckInventoryExpiry()
		{
			if (!this.EnableExpiryAlarm || this.Inventories == null)
			{
				return;
			}

			var expiryThreshold = DateTime.Today.AddDays(this.ExpiryAlarmThreshold);
			var inventoryExpired = this.Inventories.Where(i => i.Quantity > 0 && i.ExpiryTime >= DateTime.Today && i.ExpiryTime <= expiryThreshold);

			InventoryAlarmItem alarmItem = null;

			if (inventoryExpired.Count() > 0)
			{
				var alarmDetail = string.Format("当前库存中 {0} {1} 商品将在 {2} 天之内过期", inventoryExpired.Sum(i => i.Quantity), this.UnitName, this.ExpiryAlarmThreshold);

				alarmItem = new InventoryAlarmItem(this, InventoryAlarmType.Expiry, alarmDetail);
			}

			DomainEvent.Publish(new InventoryCheckedEvent(this, InventoryAlarmType.Expiry, alarmItem));
		}

		#endregion

		#region Private Methods

		private ProductInventory GetInventoryByInventoryNumber(string inventoryNumber)
		{
			//根据库存编号筛选库存记录
			return this.Inventories
				.FirstOrDefault(i => i.InventoryNumber == inventoryNumber);
		}

		#endregion
	}
}
